﻿using LightCAD.Core;
using LightCAD.Core.Elements;
using LightCAD.Runtime;
using LightCAD.Three;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Constants = LightCAD.Runtime.Constants;
using LightCAD.MathLib;
using netDxf.Tables;

namespace LightCAD.Drawing
{
    public abstract class ElementAction: ICreateDrawer,IPropertyObserver
    {
        public static class SR
        {
            public const string PointError = "需要点或选项关键字。";

        }
        public static Stack<LcBlockRef> RefStack { get; } = new Stack<LcBlockRef>();

        public SKPaint GetDrawPen(LcElement element, bool isDragCopy=false)
        {
            var doc = this.docRt.Document;
            var alllayers = doc.Layers;
            var alllinetype = doc.LineTypes;
            var Pen = Constants.defaultPen;
            var linetypename = element.LineType;
            var linewidth = element.LineWeight;
            if (linetypename == ValueFrom.ByLayer)
            {
                var layerlinetypename = alllayers.Where(x => x.Name == element.Layer).FirstOrDefault()?.LineTypeName;
                if (layerlinetypename != ValueFrom.ByLayer) {
                    var thislinetype = alllinetype.Where(x => x.LineTypeName == layerlinetypename).FirstOrDefault();
                    if (thislinetype != null)
                    {
                        var floatass = GetSKPathEffect(thislinetype);
                        if (floatass?.Length > 0)
                        {
                            Pen.PathEffect = SKPathEffect.CreateDash(floatass, 0); ;
                        }
                    }
                }
            }
            else if (linetypename == ValueFrom.ByBlock)
            {
                Pen = Constants.defaultPen;
            }
            else {
                var thislinetype = alllinetype.Where(x => x.LineTypeName == linetypename).FirstOrDefault();
                if (thislinetype != null)
                {
                    var floatass = GetSKPathEffect(thislinetype);
                    if (floatass?.Length > 0)
                    {
                        Pen.PathEffect = SKPathEffect.CreateDash(floatass, 0); ;
                    }
                }
            }
            if (linewidth == ValueFrom.ByLayer)
            {
                var layerlinewidthname = alllayers.Where(x => x.Name == element.Layer).FirstOrDefault()?.LineWeight;
                if (layerlinewidthname != null)
                {
                    Pen.StrokeWidth = GetLineTypeByEnum((LineWeight)layerlinewidthname);
                }
            }
            else if (linewidth == ValueFrom.ByBlock)
            {
                
            }
            else
            {
                Pen.StrokeWidth = GetLineType(linewidth);
            }

            if (element.RtStatus == ElementStatus.Disabled)
            {
                return Constants.disabledPen;
            }

            if (this.vportRt.IsRefEditing)
            {
                //只有在位编辑的引用对象才会正常显示，其他的都显示为Disabled
                var refObj = this.vportRt.RefEditingObject;
                foreach(var blkRef in RefStack)
                {
                    if(blkRef != refObj.Target)
                    {
                        return Constants.disabledPen;
                    }
                }
            }

            if (isDragCopy) return Pen;

            if (element.RtStatus == ElementStatus.Dragging)
            {
                return Constants.draggingPen;
            }
            else if (element.IsSelected)
            {
                return new SKPaint { Color = element.GetColorValue(), IsStroke = true, PathEffect = SKPathEffect.CreateDash(new float[] { 3, 3 }, 10) };
            }
            else
            {
                return Pen;
            }
        }

        /// <summary>
        /// 获取线宽
        /// </summary>
        /// <param name="strName"></param>
        /// <returns></returns>
        public float GetLineType(string strName)
        {
            float linewidth = 0;
            switch (strName)
            {
                case "ByLayer":
                    linewidth = 0;
                    break;
                case "ByBlock":
                    linewidth = 0;
                    break;
                case "Default":
                    linewidth = 0;
                    break;
                case "LW000":
                    linewidth = 0;
                    break;
                case "LW005":
                    linewidth = 0.05F;
                    break;
                case "LW009":
                    linewidth = 0.09F;
                    break;
                case "LW013":
                    linewidth = 0.13F;
                    break;
                case "LW015":
                    linewidth = 0.15F;
                    break;
                case "LW018":
                    linewidth = 0.18F;
                    break;
                case "LW020":
                    linewidth = 0.20F;
                    break;
                case "LW025":
                    linewidth = 0.25F;
                    break;
                case "LW030":
                    linewidth = 0.30F;
                    break;
                case "LW035":
                    linewidth = 0.35F;
                    break;
                case "LW040":
                    linewidth = 0.40F;
                    break;
                case "LW050":
                    linewidth = 0.50F;
                    break;
                case "LW053":
                    linewidth = 0.53F;
                    break;
                case "LW060":
                    linewidth = 0.60F;
                    break;
                case "LW070":
                    linewidth = 0.70F;
                    break;
                case "LW080":
                    linewidth = 0.80F;
                    break;
                case "LW090":
                    linewidth = 0.90F;
                    break;
                case "LW100":
                    linewidth = 1.00F;
                    break;
                case "LW106":
                    linewidth = 1.06F;
                    break;
                case "LW120":
                    linewidth = 1.20F;
                    break;
                case "LW140":
                    linewidth = 1.40F;
                    break;
                case "LW158":
                    linewidth = 1.58F;
                    break;
                case "LW200":
                    linewidth = 2.00F;
                    break;
                case "LW211":
                    linewidth = 2.11F;
                    break;
                default:
                    break;
            }
            return linewidth;
        }

        public float GetLineTypeByEnum(LineWeight strName)
        {
            float linewidth = 0;
            switch (strName)
            {
                case LineWeight.ByLayer:
                    linewidth = 0;
                    break;
                case LineWeight.ByBlock:
                    linewidth = 0;
                    break;
                case LineWeight.Default:
                    linewidth = 0;
                    break;
                case LineWeight.LW000:
                    linewidth = 0;
                    break;
                case LineWeight.LW005:
                    linewidth = 0.05F;
                    break;
                case LineWeight.LW009:
                    linewidth = 0.09F;
                    break;
                case LineWeight.LW013:
                    linewidth = 0.13F;
                    break;
                case LineWeight.LW015:
                    linewidth = 0.15F;
                    break;
                case LineWeight.LW018:
                    linewidth = 0.18F;
                    break;
                case LineWeight.LW020:
                    linewidth = 0.20F;
                    break;
                case LineWeight.LW025:
                    linewidth = 0.25F;
                    break;
                case LineWeight.LW030:
                    linewidth = 0.30F;
                    break;
                case LineWeight.LW035:
                    linewidth = 0.35F;
                    break;
                case LineWeight.LW040:
                    linewidth = 0.40F;
                    break;
                case LineWeight.LW050:
                    linewidth = 0.50F;
                    break;
                case LineWeight.LW053:
                    linewidth = 0.53F;
                    break;
                case LineWeight.LW060:
                    linewidth = 0.60F;
                    break;
                case LineWeight.LW070:
                    linewidth = 0.70F;
                    break;
                case LineWeight.LW080:
                    linewidth = 0.80F;
                    break;
                case LineWeight.LW090:
                    linewidth = 0.90F;
                    break;
                case LineWeight.LW100:
                    linewidth = 1.00F;
                    break;
                case LineWeight.LW106:
                    linewidth = 1.06F;
                    break;
                case LineWeight.LW120:
                    linewidth = 1.20F;
                    break;
                case LineWeight.LW140:
                    linewidth = 1.40F;
                    break;
                case LineWeight.LW158:
                    linewidth = 1.58F;
                    break;
                case LineWeight.LW200:
                    linewidth = 2.00F;
                    break;
                case LineWeight.LW211:
                    linewidth = 2.11F;
                    break;
                default:
                    break;
            }
            return linewidth;
        }

        protected IDocumentEditor docEditor;
        protected CancellationTokenSource cts;
        protected bool isFinish;
        protected DocumentRuntime docRt;
        protected ViewportRuntime vportRt;
        protected CommandCenter commandCenter;
        protected ICommandControl commandCtrl;
        protected ElementAction(IDocumentEditor docEditor)
        {
            this.docEditor = docEditor;
            this.docRt = docEditor.DocRt;
            this.vportRt = (this.docEditor as DrawingEditRuntime).ActiveViewportRt;
            this.commandCenter = this.vportRt.CommandCenter;
            this.commandCtrl = this.commandCenter.Commander;
        }
        protected ElementAction()
        {
        }

        public ElementAction SetViewport(ViewportRuntime vportRt)
        {
            this.vportRt = vportRt;
            this.commandCenter=this.vportRt.CommandCenter;
            this.commandCtrl = this.vportRt.CommandCtrl;
            this.docEditor = this.vportRt.CommandCenter.DocumentEditor;
            this.docRt = vportRt.docRt;
            return this;
        }
        protected void AttachEvents()
        {
            //docRt.ActiveViewportRt.Control.AttachEvents(this.OnViewportMouseEvent, this.OnViewportKeyDown);
            //docRt.Commander.AttachInputEvent(OnInputKeyDown, OnInputKeyUp);
        }



        protected void DetachEvents()
        {
            //docRt.ActiveViewportRt.Control.DetachEvents(this.OnViewportMouseEvent, this.OnViewportKeyDown);
            //docRt.Commander.DetachInputEvent(OnInputKeyDown, OnInputKeyUp);
        }
        protected async Task WaitFinish()
        {
            cts = new CancellationTokenSource();
            await Task.Run(() =>
            {
                while (!cts.Token.IsCancellationRequested && !isFinish) { }
            }, cts.Token);
        }
        protected virtual void OnViewportMouseEvent(string type, MouseEventRuntime e)
        {
        }
        protected virtual void OnViewportKeyDown(KeyEventRuntime e)
        {
        }
        protected virtual void OnInputKeyDown(KeyEventRuntime e)
        {
        }
        protected virtual void OnInputKeyUp(KeyEventRuntime e)
        {
        }

        public void StartCreating()
        {
            var vportRt = (this.docEditor as DrawingEditRuntime).ActiveViewportRt;
            if (vportRt.CheckStatus(ViewportStatus.InAction))
            {
                vportRt.CancelCurrentAction();
            }
            vportRt.CurrentAction = this;
            vportRt.SetStatus(ViewportStatus.InAction);
            this.commandCenter.InAction = true;
            this.vportRt.Control.SetInputText("");
            this.vportRt.Control.ShowInputText();
            this.vportRt.SetCreateDrawer(this);//设置绘制辅助线等

        }
        public void EndCreating()
        {
            vportRt.ClearStatus();
            this.vportRt.SetCreateDrawer(null);
            this.commandCtrl.Prompt(string.Empty);
            this.vportRt.CurrentAction = null;
            this.vportRt.CursorType = LcCursorType.SelectElement;
            this.vportRt.CursorAddon = CursorAddonType.None;
            this.docEditor.CommandCenter.InAction = false;
            this.vportRt.CancelCurrentAction();
            this.vportRt.Control.HideInputText();
            if (this.docRt.Action.SelectedElements.Count > 0)
            {
                this.docRt.Action.SetSelectsToDragging(false);
                this.vportRt.ClearElementGrips();
                this.docRt.Action.ClearSelects();
            }

            this.commandCtrl.SetCurMethod(null);
            this.commandCtrl.SetCurStep(null);
            //DocumentManager.Recorders.Add(this.)
            //if (IsStopRecord) return;
            //if (this.CurrentActionName == null)
            //{
            //    throw new InvalidOperationException("CurrentActionIsNull");
            //}
            //ClearRecordsFromIndx();//将索引点后面的记录清除
            //this.CurrentActionRecord.EndTime = DateTime.Now;
            //this.Records.Add(this.CurrentActionRecord);
            //this.CurrentRecordIndex++;
            //this.CurrentActionRecord = null;
            //this.CurrentActionName = null;
        }

        public virtual void DrawAuxLines(SKCanvas canvas)
        {

        }
       

        public virtual void DrawTemp(SKCanvas canvas)
        {
        }
        public virtual void CreateElement(LcElement element, Matrix3 matrix)
        {

        }
        public virtual void CreateElement(LcElement element, Vector2 basePoint, double scaleFactor)
        {

        }
        public virtual void CreateElement(LcElement element, Vector2 basePoint, Vector2 scaleVector)
        {

        }


        /// <summary>
        /// 在指定的Cavas上绘制指定的元素,2D
        /// </summary>
        /// <param name="canvas"></param>
        /// <param name="element"></param>
        /// <param name="offset"></param>
        public virtual void Draw(SKCanvas canvas, LcElement element, Vector2 offset)
        {
            this.Draw(canvas, element, Matrix3.GetTranslate(offset));
        }
        /// <summary>
        /// 在指定的Cavas上绘制指定的元素,3D
        /// </summary>
        /// <param name="canvas"></param>
        /// <param name="element"></param>
        /// <param name="matrix"></param>
        public virtual void Draw(SKCanvas canvas, LcElement element, Matrix3 matrix)
        {

        }
        public virtual void Cancel()
        {
            this.DetachEvents();
        }
        /// <summary>
        /// 获取元素的控制点(夹点)
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        public virtual ControlGrip[] GetControlGrips(LcElement element)
        {
            return null;
        }

        /// <summary>
        /// 设置拖拽控制柄状态
        /// </summary>
        /// <param name="element">拖拽的元素</param>
        /// <param name="gripName">控制柄名称，不同名称有不同处理方法</param>
        /// <param name="position">控制点的当前拖拽位置</param>
        /// <param name="isEnd">是否拖拽结束</param>
        public virtual void SetDragGrip(LcElement element, string gripName, Vector2 position, bool isEnd) { }

        /// <summary>
        /// 绘制拖拽控制点的线条
        /// </summary>
        /// <param name="canvas"></param>
        public virtual void DrawDragGrip(SKCanvas canvas) { }


        public virtual List<PropertyObserver> GetPropertyObservers()
        {
            return new List<PropertyObserver>();
        }

        private static List<PropertyObserver> _sharedPropertyObservers=PropertyAction._sharedPropertyObservers;
     

        public virtual SnapPointResult SnapPoint(SnapRuntime snapRt, LcElement element, Vector2 point, bool forRef,Vector2 PrePoint=null)
        {
            return null;
        }
        public virtual SnapPointResult SnapLine(SnapRuntime snapRt, List<Vector2> vector2s,  Vector2 point, bool forRef)
        {
            return null;
        }
     
        public LcCreateMethod SetCurMethod(LcCreateMethod[] methods, int index)
        {
            this.commandCtrl.SetCurMethod(methods[index]);
            return methods[index];
        }

        public LcCreateStep SetCurStep(LcCreateMethod method, int stepIdx)
        {
            this.commandCtrl.SetCurStep(method.Steps[stepIdx]);
            return method.Steps[stepIdx];
        }

        public float[] GetSKPathEffect(LcLineType lclinetype)
        {
            var Scale = this.vportRt.Viewport.Scale;
            var typelist = lclinetype.LineTypeDefinition.TrimStart('A').TrimStart(',').split(",");
            if (typelist.Count % 2 != 0) {
                return null;
            }
            float[] floatlist = new float[typelist.Count];
            for (int i = 0; i < typelist.Length; i++)
            {
                List<string> floatsp = typelist[i].split(".");
                if (floatsp.Count == 1)
                {
                    if (typelist[i].StartsWith('.'))
                    {
                        floatlist[i] = (float)(Scale * float.Parse("0." + floatsp[0])) * 1000;
                    }
                    else {
                        floatlist[i] = (float)(Scale * float.Parse(floatsp[0])) * 1000;
                    }
                }
                else
                {
                    floatlist[i] = (float)(Scale * float.Parse(floatsp[0] + "." + floatsp[1])) * 1000;
                }
            }
            List<float> pdlist = new List<float>();
            for (int i = 0; i < floatlist.Length; i++)
            {
                if (i == 0) {
                    pdlist.Add(floatlist[i]);
                    continue;
                }
                if (floatlist[i] > 0 && floatlist[i - 1] > 0)
                {
                    pdlist[pdlist.Count - 1] += Math.Abs(floatlist[i]);
                }
                else {
                    pdlist.Add(Math.Abs(floatlist[i]));
                }
            }
            float[] newfloatlist = pdlist.ToArray();
            return newfloatlist;
        }
    }
}
